package kiwi.core.webservices.resource;

import kiwi.core.api.config.ConfigurationService;
import kiwi.core.api.content.ContentService;
import kiwi.core.api.io.KiWiIOService;
import kiwi.core.api.io.KiWiParser;
import kiwi.core.api.io.KiWiSerializer;
import kiwi.core.api.sesame.KiWiSesameService;
import kiwi.core.api.triplestore.ResourceService;
import kiwi.core.api.triplestore.TripleStore;
import kiwi.core.exception.ParsingException;
import kiwi.core.exception.webservice.NotFoundException;
import kiwi.core.exception.webservice.ServerException;
import kiwi.core.model.Constants;
import kiwi.core.model.rdf.KiWiMediaContentLiteral;
import kiwi.core.model.rdf.KiWiResource;
import kiwi.core.model.rdf.KiWiTriple;
import kiwi.core.model.rdf.KiWiUriResource;
import kiwi.core.qualifiers.transaction.Transactional;
import org.apache.commons.io.IOUtils;
import org.openrdf.model.Statement;
import org.openrdf.model.impl.URIImpl;
import org.openrdf.repository.Repository;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.RepositoryException;
import org.openrdf.repository.RepositoryResult;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.*;

import static javax.ws.rs.core.Response.status;

/**
 * User: Thomas Kurz
 * Date: 21.02.11
 * Time: 12:00
 */
@ApplicationScoped
@Transactional
@Path("/")
public class ResourceWebService {

	private static final String MIME_PATTERN="/{mimetype:[^/]+/[^/]+}";

	@Inject
	private ConfigurationService configurationService;

	@Inject
	private ResourceService resourceService;

	@Inject
	private ContentService contentService;

	@Inject
	private KiWiIOService kiWiIOService;

	@Inject
	private TripleStore tripleStore;

	@Inject
	private KiWiSesameService kiWiSesameService;

	//******************************************* P O S T ***********************************
	//**************** POST (New or Remote)

	/**
	 * Creates new resource with given uri. If no uri is defined it creates a local uri with random uuid
	 * @param uri
	 * @return either HTTP 201(OK) or HTTP 302(FOUND)
	 */
	@POST
	@Path("resource")
	public Response postNewOrRemote(@QueryParam("uri") String uri) throws UnsupportedEncodingException {
		if(uri==null) {
			return post(configurationService.getBaseUri() + "resource/" + UUID.randomUUID().toString(), false);
		} else {
			uri = URLDecoder.decode(uri, "utf-8");
			return post(uri,true);
		}
	}

	//**************** POST (Locale)

	/**
	 * Creates new local resource with given uuid
	 * @param uuid
	 * @return either HTTP 201(OK) or HTTP 302(FOUND)
	 */
	@POST
	@Path("resource/{uuid}")
	public Response postLocal(@PathParam("uuid") String uuid) throws UnsupportedEncodingException {
		return post(configurationService.getBaseUri()+"resource/"+ uuid,false);
	}

	//**************** POST (Generic)
	private Response post(String uri, boolean remote) throws UnsupportedEncodingException {
		String location = remote ? configurationService.getBaseUri() + "resource?uri=" + URLEncoder.encode(uri, "utf-8") : uri;
		if(resourceService.getUriResource(uri) != null) {
        	Response response = status(302).build();
			response.getMetadata().add("Location",location);
			return response;
		} else {
			//create uri resource
			resourceService.createUriResource(uri);
			Response response = status(201).build();
			response.getMetadata().add("Location",location);
			return response;
		}
	}

	//******************************************* G E T ***********************************
	//**************** GET LOCALE ***********************

	/**
	 * Returns a link to a local resource (data or content) with the given uuid and an accepted return type
	 * @param uuid
	 * @param types follows the pattern (.+/.+(;rel=(content|meta))?,)+
	 * @return either HTTP 303 (See Other), HTTP 406 (Not Acceptable) or HTTP 404 (Not Found)
	 */
	@GET
	@Path("resource/{uuid}")
	public Response getLocal(@PathParam("uuid") String uuid, @HeaderParam("Accept") String types) throws UnsupportedEncodingException {
		String uri = configurationService.getBaseUri()+"resource/"+uuid;
		try {
			return get(uri, types, uuid);
		} catch (URISyntaxException e) {
			return Response.serverError().entity(e.getMessage()).build();
		}
	}

	/**
	 * Returns local resource content with the given uuid and an accepted return type
	 * @param @pathparam
	 * @param mimetype follows the pattern .+/.+
	 * @return either HTTP 303 (See Other), HTTP 406 (Not Acceptable) or HTTP 404 (Not Found)
	 */
	@GET
	@Path("content"+MIME_PATTERN+"/{uuid}")
	public Response getContentLocal(@PathParam("uuid") String uuid, @PathParam("mimetype") String mimetype) throws UnsupportedEncodingException {
		String uri = configurationService.getBaseUri()+"resource/"+uuid;
		return getContent(uri, mimetype, uuid);
	}

	/**
	 * Returns local resource data with the given uuid and an accepted return type
	 * @param uuid
	 * @param mimetype follows the pattern .+/.+
	 * @return either HTTP 303 (See Other), HTTP 406 (Not Acceptable) or HTTP 404 (Not Found)
	 */
	@GET
	@Path("meta"+MIME_PATTERN+"/{uuid}")
	public Response getMetaLocal(@PathParam("uuid") String uuid, @PathParam("mimetype") String mimetype) throws UnsupportedEncodingException {
		String uri = configurationService.getBaseUri()+"resource/"+uuid;
		return getMeta(uri, mimetype, uuid);
	}

	//**************** GET REMOTE (eq. generic) ***********************

	/**
	 * @see #getLocal(String, String) but with remote resource
	 * @param uri
	 * @param types
	 * @return
	 */
	@GET
	@Path("resource")
	public Response getRemote(@QueryParam("uri") String uri, @HeaderParam("Accept") String types) throws UnsupportedEncodingException {
		try {
			if(uri!=null) {
				return get(URLDecoder.decode(uri,"utf-8"),types,null);
			} else {
				return Response.status(400).entity("uri may not be null").build();
			}
		} catch (URISyntaxException e) {
			return Response.serverError().entity(e.getMessage()).build();
		}
	}

	/**
	 * @see #getContentLocal(String, String) but with remote resource
	 * @param uri
	 * @param mimetype
	 * @return
	 */
	@GET
	@Path("content"+MIME_PATTERN)
	public Response getContentRemote(@QueryParam("uri") String uri, @PathParam("mimetype") String mimetype) throws UnsupportedEncodingException {
		if(uri !=null) {
			return getContent(URLDecoder.decode(uri,"utf-8"),mimetype,null);
		} else {
			return Response.status(400).entity("uri may not be null").build();
		}
	}

	/**
	 * @see #getMetaLocal(String, String) but with remote resource
	 * @param uri
	 * @param mimetype
	 * @return
	 */
	@GET
	@Path("meta"+MIME_PATTERN)
	public Response getMetaRemote(@QueryParam("uri") String uri, @PathParam("mimetype") String mimetype) throws UnsupportedEncodingException {
		if(uri!=null) {
			return getMeta(URLDecoder.decode(uri,"utf-8"),mimetype,null);
		} else {
			return Response.status(400).entity("uri may not be null").build();
		}
	}

	//*********** Generic ************
	private Response get(String uri, String types, String uuid) throws URISyntaxException, UnsupportedEncodingException {
		try {
			KiWiResource resource = getUriResource(uri);
			String appendix = uuid==null?"?uri="+URLEncoder.encode(uri, "utf-8"):"/"+uuid;
			if(types == null) {
				//return links
				StringBuilder links = new StringBuilder();
				links.append(buildContentLink(resource,appendix));
				links.append(buildMetaLinks(appendix));
				Response response = Response.ok().build();
				response.getMetadata().add("Link",links.toString());
				return response;
			}
			//return redirect
			List<Type> mimes = parse(types);
			Response response = null;
			String contentmime = contentService.getContentType(resource);
			for(Type t : mimes) {
				response = buildGetResponse(appendix,t,contentmime);
				if(response.getStatus()==303) return response;
			}
			appendAcceptLinks(resource,response,kiWiIOService.getProducedType());
			return response;
		} catch (NotFoundException e) {
			return Response.status(Response.Status.NOT_FOUND).entity(e.getMessage()).build();
		} catch (ServerException e) {
			return Response.serverError().entity(e.getMessage()).build();
		}
	}

	private Response getMeta(String uri, String mimetype, String uuid) throws UnsupportedEncodingException {
		try {
			String appendix = uuid==null?"?uri="+URLEncoder.encode(uri, "utf-8"):"/"+uuid;
			KiWiResource resource = getUriResource(uri);
			//create parser
			KiWiSerializer serializer = kiWiIOService.getSerializer(mimetype);
			if(serializer == null) {
				return Response.status(406).entity("mimetype can not be processed").build();
			}
			//get and serialize triples
			Set<KiWiTriple> triples = tripleStore.listTriples(resource,null,null,null);
			Set<KiWiTriple> showT = new HashSet<KiWiTriple>();
			//remove content triple
			for(KiWiTriple t : triples) {
				if(!(t.getObject() instanceof KiWiMediaContentLiteral))
					showT.add(t);
			}
			String responseString = serializer.serializeTriples(showT);
			//build response with content type
			Response response = Response.ok(responseString).build();
			response.getMetadata().add("Content-Type",mimetype);
			String s = buildContentLink(resource,appendix);
			if(!s.equals("")) {
				response.getMetadata().add("Link",s.substring(0,s.length()-1));
			}
			return response;
		} catch (NotFoundException e) {
			return Response.status(Response.Status.NOT_FOUND).entity(e.getMessage()).build();
		} catch (ServerException e) {
			return Response.serverError().entity(e.getMessage()).build();
		}
	}

	private Response getContent(String uri, String mimetype, String uuid) throws UnsupportedEncodingException {
		try {
			String appendix = uuid==null?"?uri="+URLEncoder.encode(uri, "utf-8"):"/"+uuid;
			KiWiResource resource = getUriResource(uri);
			if(contentService.hasContent(resource,mimetype)) {
				byte[] body = contentService.getContent(resource,mimetype);
				//build response
				Response response = Response.ok(body).build();
				response.getMetadata().add("Content-Type",mimetype);
				//append data links
				String s = buildMetaLinks(appendix);
				if(s!=null) {
					response.getMetadata().add("Links",s);
				}
				return response;
			} else {
				return Response.status(406).entity("no content for mimetype "+mimetype).build();
			}
		} catch (NotFoundException e) {
			return Response.status(Response.Status.NOT_FOUND).entity(e.getMessage()).build();
		} catch (ServerException e) {
			return Response.serverError().entity(e.getMessage()).build();
		}
	}

	//******************************************* P U T ***********************************
	//**************** PUT LOCALE ***********************

	/**
	 * Sets metadata or content to a given locale resource
	 * @param uuid
	 * @param type content-type of the http body
	 * @return either HTTP 303 (See Other), HTTP 404 (Not Found), HTTP 415 (Unsupported Media Type) or HTTP 500 (Server Error)
	 */
	@PUT
	@Path("resource/{uuid}")
	public Response putLocal(@PathParam("uuid") String uuid, @HeaderParam("Content-Type") String type) throws UnsupportedEncodingException {
		String uri = configurationService.getBaseUri()+"resource/"+uuid;
		try {
			return put(uri, type, uuid);
		} catch (URISyntaxException e) {
			return Response.serverError().entity(e.getMessage()).build();
		}
	}

	/**
	 * Sets content to a given locale resource
	 * @param uuid
	 * @param mimetype content-type of the http body
	 * @param request
	 * @return either HTTP 200 (OK), HTTP 404 (Not Found), HTTP 415 (Unsupported Media Type) or HTTP 500 (Server Error)
	 */
	@PUT
	@Path("content"+MIME_PATTERN+"/{uuid}")
	public Response putContentLocal(@PathParam("uuid") String uuid, @PathParam("mimetype") String mimetype, @Context HttpServletRequest request) {
		String uri = configurationService.getBaseUri()+"resource/"+uuid;
		return putContent(uri, mimetype, request);
	}

	/**
	 * Sets metadata to a given locale resource
	 * @param uuid
	 * @param mimetype content-type of the http body
	 * @param request
	 * @return either HTTP 200 (OK), HTTP 404 (Not Found), HTTP 415 (Unsupported Media Type) or HTTP 500 (Server Error)
	 */
	@PUT
	@Path("meta"+MIME_PATTERN+"/{uuid}")
	public Response putMetaLocal(@PathParam("uuid") String uuid, @PathParam("mimetype") String mimetype, @Context HttpServletRequest request) {
		String uri = configurationService.getBaseUri()+"resource/"+uuid;
		return putMeta(uri, mimetype, request);
	}

	//**************** PUT REMOTE (eq. generic) ***********************

	/**
	 * @see #putLocal(String, String) but with remote resource
	 * @param uri
	 * @param type
	 * @return
	 */
	@PUT
	@Path("resource")
	public Response putRemote(@QueryParam("uri") String uri, @HeaderParam("Content-Type") String type) throws UnsupportedEncodingException {
		try {
			if(uri!=null) {
				return put(URLDecoder.decode(uri,"utf-8"), type, null);
			} else {
				return Response.status(400).entity("uri may not be null").build();
			}
		} catch (URISyntaxException e) {
			return Response.serverError().entity(e.getMessage()).build();
		}
	}

	/**
	 * @see #putContentLocal(String, String, javax.servlet.http.HttpServletRequest) but with remote resource
	 * @param uri
	 * @param mimetype
	 * @param request
	 * @return
	 */
	@PUT
	@Path("content"+MIME_PATTERN)
	public Response putContentRemote(@QueryParam("uri") String uri, @PathParam("mimetype") String mimetype, @Context HttpServletRequest request) throws UnsupportedEncodingException {
		if(uri!=null) {
			return putContent(URLDecoder.decode(uri,"utf-8"), mimetype, request);
		} else {
			return Response.status(400).entity("uri may not be null").build();
		}
	}

	/**
	 * @see #putMetaLocal(String, String, javax.servlet.http.HttpServletRequest) but with remote resource
	 * @param uri
	 * @param mimetype
	 * @param request
	 * @return
	 */
	@PUT
	@Path("meta"+MIME_PATTERN)
	public Response putMetaRemote(@QueryParam("uri") String uri, @PathParam("mimetype") String mimetype, @Context HttpServletRequest request) throws UnsupportedEncodingException {
		if(uri!=null) {
			return putMeta(URLDecoder.decode(uri,"utf-8"), mimetype, request);
		} else {
			return Response.status(400).entity("uri may not be null").build();
		}
	}

	private Response put(String uri, String mimetype, String uuid) throws URISyntaxException, UnsupportedEncodingException {
		try {
			KiWiResource resource = getUriResource(uri);
			String appendix = uuid==null?"?uri="+URLEncoder.encode(uri, "utf-8"):"/"+uuid;
			if(mimetype==null) {
				return Response.status(415).entity("no content-type defined").build();
			}
			List<Type> types = parse(mimetype);
			if(types.size()>0) {
				Type type = types.get(0);
				if(type.getParameter("rel") != null && type.getParameter("rel").equals("content")) {
					return Response.seeOther(new URI(configurationService.getBaseUri()+"content/"+type.getMime()+appendix)).build();
				} else if(kiWiIOService.getAcceptTypes().contains(type.getMime())) {
					return Response.seeOther(new URI(configurationService.getBaseUri()+"meta/"+type.getMime()+appendix)).build();
				}
			}
			Response response = Response.status(415).entity("type "+mimetype+" not supported").build();
			appendAcceptLinks(resource,response,kiWiIOService.getAcceptTypes());
			return response;
		} catch (NotFoundException e) {
			return Response.status(Response.Status.NOT_FOUND).entity(e.getMessage()).build();
		} catch (ServerException e) {
			return Response.serverError().entity(e.getMessage()).build();
		}
	}

	private Response putContent(String uri, String mimetype, HttpServletRequest request) {
		try {
			if( request.getContentLength() == 0 ) {
				return Response.status(400).entity("content may not be empty in resource update").build();
			}
			KiWiResource resource = getUriResource(uri);
			byte[] body = IOUtils.toByteArray(request.getInputStream());
			//store content
			contentService.setContent(resource,body,mimetype);
			return Response.ok().build();
		} catch (NotFoundException e) {
			return Response.status(Response.Status.NOT_FOUND).entity(e.getMessage()).build();
		} catch (ServerException e) {
			return Response.serverError().entity(e.getMessage()).build();
		} catch (IOException e) {
			return Response.serverError().entity("could not read request body").build();
		}
	}

	private Response putMeta(String uri, String mimetype, HttpServletRequest request) {
		try {
			//create parser
			KiWiParser parser = kiWiIOService.getParser(mimetype);
			if(parser == null) {
				return Response.status(415).entity("mimetype can not be processed").build();
			}
			if( request.getContentLength() == 0 ) {
				return Response.status(400).entity("content may not be empty in resource update").build();
			}
			KiWiResource subject = getUriResource(uri);
			//get sesame repository from input
			Repository repository = parser.parseTriples(request.getInputStream());
			RepositoryConnection connection = repository.getConnection();
			//remove sesame triples with other subject
			RepositoryResult<Statement> statements = connection.getStatements(null, null, null, true);
			while (statements.hasNext()) {
				Statement s = statements.next();
				if(s.getSubject() instanceof URIImpl) {
					if(s.getSubject().toString().equals(uri)) {
						connection.remove(s);
					}
				}
			}
			//delete all triples for given subject
			Set<KiWiTriple> old_triples = tripleStore.listTriples(subject,null,null,null);
			for(KiWiTriple old_triple : old_triples) {
				//do not remove content relation
				if(!(old_triple.getObject() instanceof KiWiMediaContentLiteral))
					tripleStore.removeTriple(old_triple);
			}
			//get kiwi triples
			kiWiSesameService.importFromSesame(connection);
			return Response.ok().build();
		} catch (NotFoundException e) {
			return Response.status(Response.Status.NOT_FOUND).entity(e.getMessage()).build();
		} catch (ServerException e) {
			return Response.serverError().entity(e.getMessage()).build();
		} catch (RepositoryException e) {
			return Response.serverError().entity(e.getMessage()).build();
		} catch (ParsingException e) {
			return Response.status(415).entity("could not parse request body").build();
		} catch (IOException e) {
			return Response.serverError().entity("could not read request body").build();
		}
	}

	//******************************************* D E L E T E ***********************************
	//**************** DELETE RESOURCE ***********************

	/**
	 * Delete remote resource with given uri
	 * @param uri
	 * @return
	 */
	@DELETE
	@Path("resource")
	public Response deleteResourceRemote(@QueryParam("uri") String uri) throws UnsupportedEncodingException {
		if (uri != null) {
			KiWiResource resource = resourceService.getUriResource(URLDecoder.decode(uri, "utf-8"));
			if (resource != null) {
				resourceService.removeResource(resource);
				Response.ok().build();
			}
			return Response.status(Response.Status.NOT_FOUND).build();
		} else {
			return Response.status(400).entity("uri may not be null").build();
		}
	}

	/**
	 * Delete local resource with given uuid
	 * @param uuid
	 * @return
	 */
	@DELETE
	@Path("resource/{uuid}")
	public Response deleteResourceLocal(@PathParam("uuid") String uuid) throws UnsupportedEncodingException {
		String uri = configurationService.getBaseUri()+"resource/"+uuid;
		return deleteResourceRemote(uri);
	}
	//**************** DELETE Content ***********************

	/**
	 * Delete content of remote resource with given uri
	 * @param uri
	 * @return
	 */
	@DELETE
	@Path("content")
	public Response deleteContentRemote(@QueryParam("uri") String uri) throws UnsupportedEncodingException {
		if (uri != null) {
			KiWiResource resource = resourceService.getUriResource(URLDecoder.decode(uri, "utf-8"));
			if (resource != null) {
				Set<KiWiTriple> triples = tripleStore.listTriples(resource, null, null, null);
				boolean found = false;
				for (KiWiTriple t : triples) {
					if (t.getObject() instanceof KiWiMediaContentLiteral) {
						tripleStore.removeTriple(t);
						found = true;
						break;
					}
				}
				return found ? Response.ok().build() : Response.status(404).entity("no content found for resource").build();
			}
			return Response.status(Response.Status.NOT_FOUND).build();
		} else {
			return Response.status(400).entity("uri may not be null").build();
		}
	}

	/**
	 * Delete content of local resource with given uuid
	 * @param uuid
	 * @return
	 */
	@DELETE
	@Path("content/{uuid}")
	public Response deleteContentLocal(@PathParam("uuid") String uuid) throws UnsupportedEncodingException {
		String uri = configurationService.getBaseUri()+"resource/"+uuid;
		return deleteContentRemote(uri);
	}
	//**************** DELETE Meta ***********************

	/**
	 * Delete metadata of remote resource with given uri
	 * @param uri
	 * @return
	 */
	@DELETE
	@Path("meta")
	public Response deleteMetaRemote(@QueryParam("uri") String uri) throws UnsupportedEncodingException {
		if (uri != null) {
			KiWiResource resource = resourceService.getUriResource(URLDecoder.decode(uri, "utf-8"));
			if (resource != null) {
				Set<KiWiTriple> triples = tripleStore.listTriples(resource, null, null, null);
				boolean found = false;
				for (KiWiTriple t : triples) {
					if (!(t.getObject() instanceof KiWiMediaContentLiteral)) {
						tripleStore.removeTriple(t);
						found = true;
						break;
					}
				}
				return found ? Response.ok().build() : Response.status(404).entity("no metadata found for resource").build();
			}
			return Response.status(Response.Status.NOT_FOUND).build();
		} else {
			return Response.status(400).entity("uri may not be null").build();
		}
	}

	/**
	 * Delete metadata of local resource with given uuid
	 * @param uuid
	 * @return
	 */
	@DELETE
	@Path("meta/{uuid}")
	public Response deleteMetaLocal(@PathParam("uuid") String uuid) throws UnsupportedEncodingException {
		String uri = configurationService.getBaseUri()+"resource/"+uuid;
		return deleteMetaRemote(uri);
	}

	//******************************************* S P E C I A L ***********************************
	//**************** LIST RESOURCES
	private static final List<String> ACCEPTED_RESOURCE_LIST_TYPES;
	static {
		ACCEPTED_RESOURCE_LIST_TYPES = new ArrayList<String>();
		ACCEPTED_RESOURCE_LIST_TYPES.add("text/html");
	}
	@GET
	@Path("resource/list")
	public Response listResources(@HeaderParam("Accept") String types) {
		List<Type> ts = parse(types);
		Type selType = null;
		for (Type t : ts) {
			if (ACCEPTED_RESOURCE_LIST_TYPES.contains(t.getMime())) {
				selType = t;
				break;
			}
		}
		if (selType != null) {
			Collection<KiWiResource> resources = resourceService.listResources();
			if (!resources.isEmpty()) {
				return Response.ok().entity(writeBody(resources,selType.getMime())).header("Content-Type", selType.getMime()).build();
			} else {
				return Response.status(404).entity("no resources to list").build();
			}
		}
		StringBuilder b = new StringBuilder();
		for (String s : ACCEPTED_RESOURCE_LIST_TYPES) {
			b.append(s);
			b.append(",");
		}
		return Response.status(406).header("Content-Type", b.toString()).build();
	}

	private String writeBody(Collection<KiWiResource> resources, String mimetype) {
		if(mimetype.equals("text/html")) {
			return writeHtmlBody(resources);
		} else {
			return "{/* Not implemented yet */}";
		}
	}

	private String writeHtmlBody(Collection<KiWiResource> resources) {
		StringBuilder b = new StringBuilder();
		b.append("<html><head></head><body>");
		b.append("<h1>List of uri and anonymous resources:</h1>");
		b.append("<table><tr style='background-color:#006D8F;font-weight:bold;color:white;'>");
		b.append("<td>Title</td><td>String</td><td>Content</td><td>Metadata</td><td>Deleted</td></tr>");
		int i = 0;
		for (KiWiResource resource : resources) {
			if (!resource.isLiteral()) {
				if (i % 2 == 0) {
					b.append("<tr style='background-color:#DFF7FF;'><td>");
				} else {
					b.append("<tr style='background-color:lightblue;'><td>");
				}
				b.append(resource.getTitle());
				b.append("</td><td>");
				b.append(resource.toString());
				b.append("</td><td>");
				//get media literal
				KiWiUriResource predicate = resourceService.getUriResource(Constants.NS_KIWI_CORE + "hasContent");
				if (predicate != null) {
					Collection<KiWiTriple> media_triples = tripleStore.listTriples(resource, predicate, null, null);
					if (!media_triples.isEmpty()) {
						try {
							KiWiMediaContentLiteral med = (KiWiMediaContentLiteral) media_triples.iterator().next().getObject();
							String url = "";
							if (resource.toString().startsWith(configurationService.getBaseUri())) {
								String uuid = resource.toString().substring((configurationService.getBaseUri() + "resource/").length());
								url = configurationService.getBaseUri() + "content/" + med.getMimeType() + "/" + uuid;
							} else {
								url = configurationService.getBaseUri() + "content/" + med.getMimeType() + "?uri=" + URLEncoder.encode(resource.toString(),"utf-8");
							}
							b.append("<a href='" + url + "'>" + url + "</a>");
						} catch (Exception e) {
							//should never happen
							b.append("some error while reading media content");
						}
					} else {
						b.append("-");
					}
				} else {
					b.append("-");
				}
				b.append("</td><td>");
				if (resource.isUriResource()) {
					String url = "";
					if (resource.toString().startsWith(configurationService.getBaseUri())) {
						String uuid = resource.toString().substring((configurationService.getBaseUri() + "resource/").length());
						url = configurationService.getBaseUri() + "meta/text/html/" + uuid;
					} else {
						try {
							url = configurationService.getBaseUri() + "meta/text/html?uri=" + URLEncoder.encode(resource.toString(),"utf-8");
						} catch(Exception e) {
							//should never happen
							url = configurationService.getBaseUri() + "meta/text/html?uri=" + resource.toString();
						}
					}
					b.append("<a href='" + url + "'>" + url + "</a>");
				} else {
					//TODO
					b.append("resource is anonymous");
				}
				b.append("</td><td>");
				b.append(resource.isDeleted());
				b.append("</td><tr>");
			}
			i++;
		}
		b.append("</table></body></html>");
		return b.toString();
	}

	//**************** UTILS ******************
	private KiWiResource getUriResource(String uri) throws NotFoundException, ServerException {
		if(uri == null) throw new ServerException("uri may not be null");
		KiWiResource resource = resourceService.getUriResource(uri);
		if(resource == null) {
			throw new NotFoundException("no resource with uri "+uri);
		}
		return resource;
	}

	private void appendAcceptLinks(KiWiResource resource, Response response, List<String> datamimes) {
		StringBuilder b = new StringBuilder();
		String mime = contentService.getContentType(resource);
		if(mime != null) {
			b.append(mime);
			b.append(";rel=content,");
		}
		for(int i=0; i<datamimes.size(); i++) {
			b.append(datamimes.get(i));
			b.append(";rel=meta");
			if(i<datamimes.size()-1) b.append(",");
		}
		response.getMetadata().add("Accept",b.toString());
	}

	private Response buildGetResponse(String appendix, Type type, String contentMime) {
		try {
			if(type.getParameter("rel") != null && type.getParameter("rel").equals("content")) {
				if(contentMime != null && type.getMime().equals(contentMime)) {
					return Response.seeOther(new URI(configurationService.getBaseUri()+"content/"+contentMime+appendix)).build();
				} else {
					return Response.status(406).build();
				}
			}
			else if(kiWiIOService.getProducedType().contains(type.getMime())) {
				return Response.seeOther(new URI(configurationService.getBaseUri()+"meta/"+type.getMime()+appendix)).build();
			}
		} catch (Exception e) {
			return Response.serverError().build();
		}
		return Response.status(406).build();
	}

	private String buildMetaLinks(String appendix) {
		StringBuilder b = new StringBuilder();
		List<String> datamimes = kiWiIOService.getProducedType();
		for(int i=0; i<datamimes.size(); i++) {
			b.append("<");
			b.append(configurationService.getBaseUri()+"meta/"+datamimes.get(i)+appendix);
			b.append(">");
			b.append(";type=");
			b.append(datamimes.get(i));
			b.append(";rel=meta");
			if(i<datamimes.size()-1) b.append(",");
		}
		return b.toString();
	}

	private String buildContentLink(KiWiResource resource, String appendix) {
		//test if there is content
		StringBuffer b = new StringBuffer();
		String mime = contentService.getContentType(resource);
		if(mime != null) {
			b.append("<");
			b.append(configurationService.getBaseUri()+"content/"+mime+appendix);
			b.append(">");
			b.append(";type=");
			b.append(mime);
			b.append(";rel=content");
			b.append(",");
		}
		return b.toString();
	}

	private List<Type> parse(String type) {
		List<Type> l = new ArrayList<Type>();
		if (type != null) {
			String[] ts = type.split(",");
			for (String t : ts) {
				String[] vars = t.split(";");
				String mime = vars[0].trim();
				System.out.println(mime);
				Map<String, String> p = new HashMap<String, String>();
				for (int i = 1; i < vars.length; i++) {
					String[] v = vars[i].trim().split("=");
					String property = v[0].trim();
					String value = v[1];
					if (v[1].contains("\"")) {
						value = value.trim().substring(1, value.length() - 1);
					}
					p.put(property, value);
				}
				l.add(new Type(mime, p));
			}
		}
		return l;
	}

	class Type {
		String mime;
		Map<String,String> parameters;
		public Type(String mime, Map<String,String>parameters) {
			this.mime = mime;
			this.parameters = parameters;
		}

		public String getMime() {
			return mime;
		}

		public boolean containsParameter(String key) {
			return parameters.containsKey(key);
		}

		public String getParameter(String key) {
			return parameters.get(key);
		}
	}
}
